/*
1.条件变量std::condition_variable  wait()    notify_one()
	线程a：等待一个条件的满足
	线程b：专门往消息队列中仍数据

	线程b的操作满足了a的条件，a继续往下执行

	condition-variable是一个和条件相关的类，等待条件达成
	这个类要与互斥量配合工作


	wait（uniquelock对象，lambda）是条件变量类的一个成员函数

	如果第二个参数lambda表达式的返回值是true，那么wait（）直接返回，程序继续执行

	如果第二个参数lambda表达式的返回值是false，那么wait（）将解锁互斥量并堵塞
	到本行。堵塞到其他某个线程调用notify-one（）成员函数为止，原程序继续执行

	如果wait没有第二个参数
	那么就跟第二个参数返回false相同，（解锁互斥量并堵塞
	到本行。堵塞到其他某个线程调用notify-one（）成员函数为止，原程序继续执行）

	其他线程用notify-one（）将本线程唤醒后
	wait（）再次执行：
	a/不断尝试重新获取互斥量锁并上锁，如果获取不到，会卡住，一直尝试
	b/	如果wait有第二个参数，就判断这个参数的返回值，并重复wait（）的内部操作
		直到得到true的返回结果程序继续运行
		如果没有第二个参数，则相对于里面有一个true，程序继续执行（与没有唤醒时不一样）

2.上述代码的深入思考
	代码并不是依次执行的，我们可能插入了几百条数据，却在另一个线程中只取出了几个
	数据。
	//本着稳定的基本原则，我们宁愿放弃使用条件变量，转而使用双重检查

3.成员函数notify_all()
	通知所有线程


	使用notify-one（）时，我们使用一个线程入口函数（内部使用相同的条件变量）
	创建了多个线程，此时我们不知道具体哪一个线程被唤醒
	notify-all（）可以唤醒所有的使用了相同条件变量的线程
	但是如果两个wait完全相同
	他们都被唤醒后，都去获取锁，其中有一个可以获取锁往下运行，另一个
	拿不到锁，卡住
	如果两个wait不完全相同，使用相同的条件变量，则可以同时唤醒，同时工作

*/


#include<iostream>
#include<thread>
#include<vector>
#include <list>
#include<mutex>
using namespace std;

class A {
public:

	//线程入口，读入消息队列
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; ++i) {
			cout << "inMsgRecvQueue()执行，插入一个元素" << i << endl;
			unique_lock<mutex> my_guard(my_mutex, try_to_lock);
			msgRecvQueue.push_back(i);
			//...
			//处理其他代码

			//notify-one唤醒wait进程
			//弊端：如果此时outMsgLULProc正在处理事务，并且消耗一段时间，
			//而不是在wait（）处等待，则这个唤醒无效
			my_condition.notify_one();
		}
		return;
	}


	//共享数据操作，提取到一个方法中
	bool outMsgLULProc(int &command) {
		//语法与lockguard类似


		//因为每次都要尝试加锁，效率较低
		//可以利用双重锁定来提高提高效率（外层加一层if (!msgRecvQueue.empty())
		//但是一直循环不是好的写法
		//我们希望消息队列不为空的时候，有东西去通知我们去取数据
		//用于condition-variable

		//unique_lock<mutex>(my_mutex);
		//if (!msgRecvQueue.empty()) {
		//	//消息不为空
		//	command = msgRecvQueue.front();
		//	msgRecvQueue.pop_front();
		//	return true;
		//}
		//else {
		//	return false;
		//}

	}

	//线程入口，取出消息队列
	void outMsgRecvQueue() {

		int command = 0;

		while (true) {
			unique_lock<mutex> sbguard(my_mutex);
			//wait(uniquelock对象，lambda表达式/函数对象）
			//
			my_condition.wait(sbguard, [this] {
				if (!msgRecvQueue.empty())
				{
					return true;
				}
				return false;
				}
			);
			//程序经过这一步，互斥锁一定是锁着的
			//此时我们可以输出数据并解锁，以便其他线程使用共享数据
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			sbguard.unlock();
			cout << "取出元素" << command << endl;

		}



		//for (int i = 0; i < 100000; ++i) {
		//	if (outMsgLULProc(command)) {
		//		//具体操作
		//		cout << "具体操作,取出一个元素" << command << endl;
		//	}
		//	else {
		//		cout << "消息为空" << i << endl;
		//	}
		//}
		//cout << "消息处理结束" << endl;
	}

private:
	list<int> msgRecvQueue;
	mutex my_mutex;
	//创建一个条件变量对象
	condition_variable my_condition;

};


int main(int argc, char* argv[]) {

	A myobja;
	//相同入口函数的两个线程，被唤醒的线程不一定
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myOutMsgObj2(&A::outMsgRecvQueue, &myobja);

	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutMsgObj.join();
	myOutMsgObj2.join();
	myInMsgObj.join();

	return 0;
}